package com.asr.service;

import com.asr.config.AsrProperties;
import com.jcraft.jsch.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.function.Consumer;

@Slf4j
@Service
@RequiredArgsConstructor
public class SshService {
    
    private final AsrProperties asrProperties;
    
    /**
     * 创建 SSH 会话
     */
    private Session createSession() throws JSchException {
        AsrProperties.Remote remote = asrProperties.getRemote();
        
        JSch jsch = new JSch();
        
        // 如果配置了私钥，使用私钥认证
        if (remote.getPrivateKey() != null && !remote.getPrivateKey().isEmpty()) {
            jsch.addIdentity(remote.getPrivateKey());
        }
        
        Session session = jsch.getSession(remote.getUsername(), remote.getHost(), remote.getPort());
        
        // 如果配置了密码，使用密码认证
        if (remote.getPassword() != null && !remote.getPassword().isEmpty()) {
            session.setPassword(remote.getPassword());
        }
        
        // SSH 配置
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        
        session.connect(30000); // 30秒超时
        log.info("SSH 连接成功: {}@{}:{}", remote.getUsername(), remote.getHost(), remote.getPort());
        
        return session;
    }
    
    /**
     * 执行远程命令
     */
    public CommandResult executeCommand(String command) throws JSchException, IOException {
        Session session = null;
        ChannelExec channel = null;
        
        try {
            session = createSession();
            channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(command);
            
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
            
            channel.setOutputStream(outputStream);
            channel.setErrStream(errorStream);
            
            channel.connect();
            
            // 等待命令执行完成
            while (!channel.isClosed()) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            
            int exitCode = channel.getExitStatus();
            String output = outputStream.toString(StandardCharsets.UTF_8);
            String error = errorStream.toString(StandardCharsets.UTF_8);
            
            log.debug("命令执行完成，退出码: {}", exitCode);
            
            return new CommandResult(exitCode, output, error);
            
        } finally {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
    
    /**
     * 执行远程命令并实时读取输出
     */
    public CommandResult executeCommandWithCallback(String command, Consumer<String> outputCallback) 
            throws JSchException, IOException {
        Session session = null;
        ChannelExec channel = null;
        
        try {
            session = createSession();
            channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(command);
            
            InputStream inputStream = channel.getInputStream();
            InputStream errorStream = channel.getErrStream();
            
            channel.connect();
            
            StringBuilder output = new StringBuilder();
            StringBuilder error = new StringBuilder();
            
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8));
            
            // 读取输出
            String line;
            while (true) {
                // 读取标准输出
                while (reader.ready() && (line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                    if (outputCallback != null) {
                        outputCallback.accept(line);
                    }
                }
                
                // 读取错误输出
                while (errorReader.ready() && (line = errorReader.readLine()) != null) {
                    error.append(line).append("\n");
                    log.debug("错误输出: {}", line);
                }
                
                // 检查是否完成
                if (channel.isClosed()) {
                    // 读取剩余输出
                    while ((line = reader.readLine()) != null) {
                        output.append(line).append("\n");
                        if (outputCallback != null) {
                            outputCallback.accept(line);
                        }
                    }
                    while ((line = errorReader.readLine()) != null) {
                        error.append(line).append("\n");
                    }
                    break;
                }
                
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            
            int exitCode = channel.getExitStatus();
            log.debug("命令执行完成，退出码: {}", exitCode);
            
            return new CommandResult(exitCode, output.toString(), error.toString());
            
        } finally {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
    
    /**
     * 上传文件到远程服务器
     */
    public void uploadFile(String localPath, String remotePath) throws JSchException, SftpException {
        Session session = null;
        ChannelSftp channel = null;
        
        try {
            session = createSession();
            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            
            // 确保远程目录存在
            String remoteDir = remotePath.substring(0, remotePath.lastIndexOf('/'));
            try {
                channel.mkdir(remoteDir);
            } catch (SftpException e) {
                // 目录可能已存在，忽略错误
            }
            
            channel.put(localPath, remotePath);
            log.info("文件上传成功: {} -> {}", localPath, remotePath);
            
        } finally {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
    
    /**
     * 上传文件（从 MultipartFile）
     */
    public void uploadFile(MultipartFile file, String remotePath) throws JSchException, SftpException, IOException {
        Session session = null;
        ChannelSftp channel = null;
        
        try {
            session = createSession();
            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            
            // 确保远程目录存在
            String remoteDir = remotePath.substring(0, remotePath.lastIndexOf('/'));
            createRemoteDirectory(channel, remoteDir);
            
            // 上传文件
            channel.put(file.getInputStream(), remotePath);
            log.info("文件上传成功: {} -> {}", file.getOriginalFilename(), remotePath);
            
        } finally {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
    
    /**
     * 下载文件从远程服务器
     */
    public void downloadFile(String remotePath, String localPath) throws JSchException, SftpException {
        Session session = null;
        ChannelSftp channel = null;
        
        try {
            session = createSession();
            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            
            channel.get(remotePath, localPath);
            log.info("文件下载成功: {} -> {}", remotePath, localPath);
            
        } finally {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
    
    /**
     * 创建远程目录（递归）
     */
    private void createRemoteDirectory(ChannelSftp channel, String path) {
        try {
            channel.cd(path);
        } catch (SftpException e) {
            // 目录不存在，创建它
            String[] dirs = path.split("/");
            StringBuilder currentPath = new StringBuilder();
            
            for (String dir : dirs) {
                if (dir.isEmpty()) continue;
                
                currentPath.append("/").append(dir);
                try {
                    channel.cd(currentPath.toString());
                } catch (SftpException ex) {
                    try {
                        channel.mkdir(currentPath.toString());
                        channel.cd(currentPath.toString());
                    } catch (SftpException ex2) {
                        log.warn("创建目录失败: {}", currentPath);
                    }
                }
            }
        }
    }
    
    /**
     * 命令执行结果
     */
    public static class CommandResult {
        private final int exitCode;
        private final String output;
        private final String error;
        
        public CommandResult(int exitCode, String output, String error) {
            this.exitCode = exitCode;
            this.output = output;
            this.error = error;
        }
        
        public int getExitCode() {
            return exitCode;
        }
        
        public String getOutput() {
            return output;
        }
        
        public String getError() {
            return error;
        }
        
        public boolean isSuccess() {
            return exitCode == 0;
        }
    }
}
